<?php
/**
 * snowflake 服务提供者
 * @author: langming
 * @date: 2021/8/26 下午1:49
 */

namespace Langming\Snowflake\Providers;

use Langming\Snowflake\Snowflake;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
use Langming\Snowflake\Exceptions\BootException;

class SnowflakeProvide extends ServiceProvider
{

    /**
     * 优先注册
     */
    public function register()
    {
        //覆盖octane配置文件，定义table
        $this->mergeConfigFrom(
            __DIR__ . '/../../config/tables.php', 'octane.tables'
        );

        //以单例模式注入到容器， 因配置文件在boot中还需要更改，故不以构造函数参数的形式传入
        $this->app->singleton(Snowflake::class, function () {
            return new Snowflake();
        });
    }

    public function boot()
    {
        //数据迁移
        $this->loadMigrationsFrom(__DIR__ . '/../../migrations');

        //项目发布配置文件
        if (!file_exists(config_path('snowflake.php'))) {
            $this->publishes([
                __DIR__ . '/../../config/snowflake.php' => config_path('snowflake.php'),
            ], 'snowflake-config');
        }

        //读取配置
        $config = config('snowflake');

        //动态获得workerid
        $config['worker_id'] = $this->getWorkId($config);
        config(['snowflake.worker_id' => $config['worker_id']]);

        //将单例类按配置更新属性值
        $Snowflake = $this->app->get(Snowflake::class);
        $Snowflake->config($config);
    }

    /**
     * workerid的自动注册和获取
     * @param array $config
     * @return int
     */
    private function getWorkId(array $config): int
    {
        //获取当前机器mac地址
        $mac = json_encode(swoole_get_local_mac());
        try {
            $snowflakeTable = DB::connection($config['db_connection'])->table($config['db_table']);
            $where = [
                ['datacenter_id', $config['data_center_id']],
                ['mac', $mac]
            ];
            $workerItem = $snowflakeTable->where($where)->first();
            if ($workerItem) {
                return $workerItem->id;
            } else {
                //对datacenterid最大值判断
                if ($config['data_center_id'] > (-1 ^ (-1 << $config['data_center_id_bits']))){
                    throw new BootException("The maximum number of snowflake datacenter has been exceeded");
                }

                //对已存在的workerid和配置文件中最大值做比较
                $max = $snowflakeTable->where($where)->count();
                if ($max >= (-1 ^ (-1 << $config['worker_id_bits']))) {
                    throw new BootException("The maximum number of snowflake workers has been exceeded");
                } else {
                    $id = $snowflakeTable->insertGetId([
                        'mac' => $mac,
                        'datacenter_id' => $config['data_center_id'],
                        'created_at' => date('Y-m-d H:i:s')
                    ]);
                    return $id;
                }
            }
        } catch (BootException $e) {
            echo $e->getMessage() . "\n";
        }
    }
}
